﻿using System;
using System.Collections.Generic;
using System.Linq;
using msilgc.cbuilder.MSILSyntaxTree;
using System.Reflection.Emit;

namespace msilgc.cbuilder.CBuilder
{
    internal class TranslatorFactory {
        private readonly IDictionary<OpCode, ITranslator> _translators;

        public TranslatorFactory(VariableFactory varFactory) {
            LoadConstantTranslator loadConstTranslator = new LoadConstantTranslator(varFactory);
            LoadArgumentFactory argTranslator = new LoadArgumentFactory(varFactory);
            AssignToLocalVariableFactory assignLocalTranslator = new AssignToLocalVariableFactory();
            LoadLocalTranslator loadLocalTranslator = new LoadLocalTranslator(varFactory);
            LoadArrayElementTranslator arrayTranslator = new LoadArrayElementTranslator(varFactory);
            ArithmeticTranslator arithmeticTranslator = new ArithmeticTranslator(varFactory);
            StoreArrayElementTranslator storeArrayTranslator = new StoreArrayElementTranslator();
            ArrayLengthTranslator arrayLenTranslator = new ArrayLengthTranslator(varFactory);
            CastStatementBuilder castTranslator = new CastStatementBuilder(varFactory);
            BooleanBranchTranslator boolBranchTranslator = new BooleanBranchTranslator();
            ReturnTranslator returnTranslator = new ReturnTranslator();
            UnconditionalGotoStatementFactory gotoFactory = new UnconditionalGotoStatementFactory();
            NoopTranslator noopTranslator = new NoopTranslator();
            LoadArgumentReferenceTranslator argRefTranslator = new LoadArgumentReferenceTranslator(varFactory);
            LoadFieldTranslator loadFieldTranslator = new LoadFieldTranslator(varFactory);
            LoadElementAddressAsTranslator loadElementAsTranslator = new LoadElementAddressAsTranslator(varFactory);
            DupTranslator dupTranslator = new DupTranslator(varFactory);
            LoadObjectTranslator loadObjTranslator = new LoadObjectTranslator(varFactory);
            StoreObjectTranslator storeObjTranslator = new StoreObjectTranslator();
            MethodCallTranslator methodTranslator = new MethodCallTranslator(varFactory);
            ConditionalBranchTranslator condBranchTranslator = new ConditionalBranchTranslator();
            NegationTranslator negTranslator = new NegationTranslator(varFactory);
            NotTranslator notTranslator = new NotTranslator(varFactory);
            SwitchTranslator switchTranslator = new SwitchTranslator();
            _translators = new Dictionary<OpCode, ITranslator> {
                   {OpCodes.Switch, switchTranslator},
                   {OpCodes.Not, notTranslator},
                   {OpCodes.Neg, negTranslator},
                   {OpCodes.Beq, condBranchTranslator},
                   {OpCodes.Beq_S, condBranchTranslator},
                   {OpCodes.Bge, condBranchTranslator},
                   {OpCodes.Bge_S, condBranchTranslator},
                   {OpCodes.Bge_Un, condBranchTranslator},
                   {OpCodes.Bge_Un_S, condBranchTranslator},
                   {OpCodes.Bgt, condBranchTranslator},
                   {OpCodes.Bgt_S, condBranchTranslator},
                   {OpCodes.Bgt_Un, condBranchTranslator},
                   {OpCodes.Bgt_Un_S, condBranchTranslator},
                   {OpCodes.Ble, condBranchTranslator},
                   {OpCodes.Ble_S, condBranchTranslator},
                   {OpCodes.Ble_Un, condBranchTranslator},
                   {OpCodes.Ble_Un_S, condBranchTranslator},
                   {OpCodes.Blt, condBranchTranslator},
                   {OpCodes.Blt_S, condBranchTranslator},
                   {OpCodes.Blt_Un, condBranchTranslator},
                   {OpCodes.Blt_Un_S, condBranchTranslator},
                   {OpCodes.Bne_Un, condBranchTranslator},
                   {OpCodes.Bne_Un_S, condBranchTranslator},
                   {OpCodes.Call, methodTranslator},
                   {OpCodes.Calli, methodTranslator},
                   {OpCodes.Callvirt, methodTranslator},
                   {OpCodes.Ldc_I4_0, loadConstTranslator},
                   {OpCodes.Ldc_I4_1, loadConstTranslator},
                   {OpCodes.Ldc_I4_2, loadConstTranslator},
                   {OpCodes.Ldc_I4_3, loadConstTranslator},
                   {OpCodes.Ldc_I4_4, loadConstTranslator},
                   {OpCodes.Ldc_I4_5, loadConstTranslator},
                   {OpCodes.Ldc_I4_6, loadConstTranslator},
                   {OpCodes.Ldc_I4_7, loadConstTranslator},
                   {OpCodes.Ldc_I4_8, loadConstTranslator},
                   {OpCodes.Ldc_R4, loadConstTranslator},
                   {OpCodes.Ldc_R8, loadConstTranslator},
                   {OpCodes.Ldc_I4, loadConstTranslator},
                   {OpCodes.Ldc_I4_M1, loadConstTranslator},
                   {OpCodes.Ldc_I4_S, loadConstTranslator},
                   {OpCodes.Ldc_I8, loadConstTranslator},
                   {OpCodes.Ldstr, loadConstTranslator},
                   {OpCodes.Nop, noopTranslator},
                   {OpCodes.Stloc, assignLocalTranslator},
                   {OpCodes.Stloc_0, assignLocalTranslator},
                   {OpCodes.Stloc_1, assignLocalTranslator},
                   {OpCodes.Stloc_2, assignLocalTranslator},
                   {OpCodes.Stloc_3, assignLocalTranslator},
                   {OpCodes.Stloc_S, assignLocalTranslator},
                   {OpCodes.Ldarg_0, argTranslator},
                   {OpCodes.Ldarg_1, argTranslator},
                   {OpCodes.Ldarg_2, argTranslator},
                   {OpCodes.Ldarg_3, argTranslator},
                   {OpCodes.Ldarg, argTranslator},
                   {OpCodes.Ldarg_S, argTranslator},
                   {OpCodes.Br_S, gotoFactory},
                   {OpCodes.Br, gotoFactory},
                   {OpCodes.Ldloc_0, loadLocalTranslator},
                   {OpCodes.Ldloc_1, loadLocalTranslator},
                   {OpCodes.Ldloc_2, loadLocalTranslator},
                   {OpCodes.Ldloc_3, loadLocalTranslator},
                   {OpCodes.Ldloc_S, loadLocalTranslator},
                   {OpCodes.Ldelem_I, arrayTranslator},
                   {OpCodes.Ldelem_I1, arrayTranslator},
                   {OpCodes.Ldelem_I2, arrayTranslator},
                   {OpCodes.Ldelem_I4, arrayTranslator},
                   {OpCodes.Ldelem_I8, arrayTranslator},
                   {OpCodes.Ldelem_R4, arrayTranslator},
                   {OpCodes.Ldelem_R8, arrayTranslator},
                   {OpCodes.Ldelem_U1, arrayTranslator},
                   {OpCodes.Ldelem_U2, arrayTranslator},
                   {OpCodes.Ldelem_U4, arrayTranslator},
                   {OpCodes.Add, arithmeticTranslator},
                   {OpCodes.Add_Ovf, arithmeticTranslator},
                   {OpCodes.Add_Ovf_Un, arithmeticTranslator},
                   {OpCodes.And, arithmeticTranslator},
                   {OpCodes.Ceq, arithmeticTranslator},
                   {OpCodes.Cgt, arithmeticTranslator},
                   {OpCodes.Clt, arithmeticTranslator},
                   {OpCodes.Cgt_Un, arithmeticTranslator},
                   {OpCodes.Clt_Un, arithmeticTranslator},
                   {OpCodes.Div, arithmeticTranslator},
                   {OpCodes.Div_Un, arithmeticTranslator},
                   {OpCodes.Mul, arithmeticTranslator},
                   {OpCodes.Mul_Ovf, arithmeticTranslator},
                   {OpCodes.Mul_Ovf_Un, arithmeticTranslator},
                   {OpCodes.Or, arithmeticTranslator},
                   {OpCodes.Rem, arithmeticTranslator},
                   {OpCodes.Rem_Un, arithmeticTranslator},
                   {OpCodes.Sub, arithmeticTranslator},
                   {OpCodes.Sub_Ovf, arithmeticTranslator},
                   {OpCodes.Sub_Ovf_Un, arithmeticTranslator},
                   {OpCodes.Xor, arithmeticTranslator},
                   {OpCodes.Stelem_I, storeArrayTranslator},
                   {OpCodes.Stelem_I1, storeArrayTranslator},
                   {OpCodes.Stelem_I2, storeArrayTranslator},
                   {OpCodes.Stelem_I4, storeArrayTranslator},
                   {OpCodes.Stelem_I8, storeArrayTranslator},
                   {OpCodes.Stelem_R4, storeArrayTranslator},
                   {OpCodes.Stelem_R8, storeArrayTranslator},
                   {OpCodes.Ldlen, arrayLenTranslator},
                   {OpCodes.Conv_I, castTranslator},
                   {OpCodes.Conv_I1, castTranslator},
                   {OpCodes.Conv_I2, castTranslator},
                   {OpCodes.Conv_I4, castTranslator},
                   {OpCodes.Conv_I8, castTranslator},
                   {OpCodes.Conv_Ovf_I, castTranslator},
                   {OpCodes.Conv_Ovf_I_Un, castTranslator},
                   {OpCodes.Conv_Ovf_I1, castTranslator},
                   {OpCodes.Conv_Ovf_I1_Un, castTranslator},
                   {OpCodes.Conv_Ovf_I2, castTranslator},
                   {OpCodes.Conv_Ovf_I2_Un, castTranslator},
                   {OpCodes.Conv_Ovf_I4, castTranslator},
                   {OpCodes.Conv_Ovf_I4_Un, castTranslator},
                   {OpCodes.Conv_Ovf_I8, castTranslator},
                   {OpCodes.Conv_Ovf_I8_Un, castTranslator},
                   {OpCodes.Conv_Ovf_U, castTranslator},
                   {OpCodes.Conv_Ovf_U_Un, castTranslator},
                   {OpCodes.Conv_Ovf_U1, castTranslator},
                   {OpCodes.Conv_Ovf_U1_Un, castTranslator},
                   {OpCodes.Conv_Ovf_U2, castTranslator},
                   {OpCodes.Conv_Ovf_U2_Un, castTranslator},
                   {OpCodes.Conv_Ovf_U4, castTranslator},
                   {OpCodes.Conv_Ovf_U4_Un, castTranslator},
                   {OpCodes.Conv_Ovf_U8, castTranslator},
                   {OpCodes.Conv_Ovf_U8_Un, castTranslator},
                   {OpCodes.Conv_R_Un, castTranslator},
                   {OpCodes.Conv_R4, castTranslator},
                   {OpCodes.Conv_R8, castTranslator},
                   {OpCodes.Conv_U, castTranslator},
                   {OpCodes.Conv_U1, castTranslator},
                   {OpCodes.Conv_U2, castTranslator},
                   {OpCodes.Conv_U4, castTranslator},
                   {OpCodes.Conv_U8, castTranslator},
                   {OpCodes.Brfalse, boolBranchTranslator},
                   {OpCodes.Brfalse_S, boolBranchTranslator},
                   {OpCodes.Brtrue, boolBranchTranslator},
                   {OpCodes.Brtrue_S, boolBranchTranslator},
                   {OpCodes.Ret, returnTranslator},
                   {OpCodes.Ldarga, argRefTranslator},
                   {OpCodes.Ldarga_S, argRefTranslator},
                   {OpCodes.Ldfld, loadFieldTranslator},
                   {OpCodes.Ldelema, loadElementAsTranslator},
                   {OpCodes.Dup, dupTranslator},
                   {OpCodes.Ldobj, loadObjTranslator},
                   {OpCodes.Stobj, storeObjTranslator},
               };
        }

        public IEnumerable<OpCode> UnsupportedOpCodes
        {
            get 
            {
                return typeof(OpCodes).GetFields()
                    .Select(field => (OpCode)field.GetValue(null))
                    .Where(instruction => !_translators.ContainsKey(instruction));
            }
        }

        public IEnumerable<OpCode> SupportedOpCodes
        {
            get { return _translators.Keys; }
        }

        public ITranslator CreateTranslator(Instruction inst) {
            if (!_translators.ContainsKey(inst.Inst)) {
                throw new Exception("no translator for: " + inst.ToString());
            }
            return _translators[inst.Inst];
        }
    }
}